#include <stdlib.h>
#include <string.h>

#include "list.h"

/* name: list_init
 * Initialize the list.
 */
void list_init(List *list, void (*destroy)(void *data))
{
  list->size = 0;
  list->destroy = destroy;
  list->head = NULL;
  list->tail = NULL;

  return;
}

/* name: list_destroy
 *  Remove each element.
 */
void list_destroy(List *list)
{
  void *data;
  while (list_size(list) > 0)
    if (0 == list_rem_next(list, NULL, (void **)&data)
	&& list->destroy != NULL) {
      /* Call a user-defined function to free */
      /* dynamically allocated data. */
      list->destroy(data);
    }
  /* No operations are allowed now, but clear the
   * structure as a precaution.
   */
  memset(list, 0, sizeof(List));
 
  return;
}

/* name: list_ins_next
 */
int list_ins_next(List *list, ListElmt *element, const void *data)
{
  ListElmt *new_element;

  /* Allocate storage for the element */
  if (NULL == (new_element = (ListElmt *)malloc(sizeof(ListElmt))))
    return -1;

  /* Insert the element into the list */
  new_element->data = (void *)data;

  if (NULL == element) {
    /* Handle insertion at the head of the list */
    if (0 == list_size(list))
      list->tail = new_element;

    new_element->next = list->head;
    list->head = new_element;
  } else {
    /* Handle insertion somewhere other than at the head */
    if (NULL == element->next)
      list->tail = new_element;

    new_element->next = element->next;
    element->next = new_element;
  }

  /* Adjust the size of the list to account for the inserted element */
  list->size++;
  
  return 0;
}

/* name: list_rem_next
 */
int list_rem_next(List *list, ListElmt *element, void **data)
{
  ListElmt *old_element;

  /* Do not allow removal from an empty list */
  if (0 == list_size(list))
    return -1;

  /* Remove the element from the list */
  if (NULL == element) {
    /* Handle removal from the head of the list */
    *data = list->head->data;
    old_element = list->head;
    list->head = list->head->next;

    if (1 == list_size(list)) {
      list->tail = NULL;
    }
  } else {
    /* Handle removal from somewhere other than the head */
    if (element->next == NULL)
      return -1;

    *data = element->next->data;
    old_element = element->next;
    element->next = element->next->next;

    if (element->next == NULL)
      list->tail = element;
  }

  /* Free the storage allocated by the abstract datatype */
  free(old_element);

  /* Adjust the size of the list to account for the removed element */
  list->size--;

  return 0;
}

/* name: list_reverse
 */
void list_reverse(List *list)
{
  ListElmt *node = NULL;	/* pointer for old list element */
  ListElmt *new = NULL;		/* head of new list */
  ListElmt *old = NULL;		/* head of old list */
  if (NULL == list) return ;
  if (NULL == list->head) return ;

  list->tail = list->head;
  old = list->head;
  node = old;
  while ( old != NULL ) {
    /* move out a note from old list */
    old = old->next;

    /* insert the note into new list */
    node->next = new;
    new = node;

    /* prepare the next node */
    node = old;
  }
  
  list->head = new;
}
